//
// Created by QU on 24-6-13.
//
#include <iostream>
#include <fstream>
#include <sstream>
#include <functional>
#include <iomanip>
#include <utility>
#include <memory>
#include <vector>
#include <map>
#include <set>

using std::cout, std::cin, std::endl, std::unitbuf;

int main(int argc, char const* argv[])
{
    /// 格式化IO的控制
    // 除了流状态之外, c++的iostream对象维护一个格式状态来控制IO如何格式化的细节。
    // 格式状态控制格式化的某些方面，如整型值是几进制、浮点值的精度、一个输出元素的宽度等。

    // 标准库定义了一组操纵符（manipulator）（参见1.2节，第6页）来修改流的格式状态，
    // 我们已经在程序中使用过一个操纵符——endl，我们将它“写”到输出流，就像它是一个值一样。
    // 但endl不是一个普通值，而是一个操作：它输出一个换行符并刷新缓冲区

    // 当操纵符改变流的格式状态时，通常改变后的状态对所有后续IO都生效
    std::cout << "default bool status: " << true << " " << false << std::endl;
    std::cout << "using 'boolalpha' stream operator" << std::boolalpha << true << " " << false << std::endl;


    /////////  其中许多操作符定义在iomanip文件.

    // std::boolalpha 和 std::noboolalpha
    // 作用：控制布尔值的显示方式。
    std::cout << std::boolalpha << true << " " << false << std::endl; // 输出：true false
    std::cout << std::noboolalpha << true << " " << false << std::endl; // 输出：1 0

    // std::showbase 和 std::noshowbase
    // 作用：控制是否显示整数的基数前缀（如 0x for hex, 0 for octal）。
    std::cout << std::showbase << std::hex << 255 << std::endl; // 输出：0xff
    std::cout << std::noshowbase << std::hex << 255 << std::endl; // 输出：ff

    // std::showpoint 和 std::noshowpoint
    // 作用：控制浮点数是否显示小数点。
    std::cout << std::showpoint << 10.0 << std::endl; // 输出：10.0000
    std::cout << std::noshowpoint << 10.0 << std::endl; // 输出：10

    // std::showpos 和 std::noshowpos
    // 作用：控制正数是否显示加号。
    std::cout << std::showpos << 10 << std::endl; // 输出：+10
    std::cout << std::noshowpos << 10 << std::endl; // 输出：10

    // std::uppercase 和 std::nouppercase
    // 作用：控制十六进制数字和科学计数法中的 'e' 是否大写。
    std::cout << std::uppercase << std::hex << 255 << std::endl; // 输出：FF
    std::cout << std::nouppercase << std::hex << 255 << std::endl; // 输出：ff

    // std::dec、std::hex 和 std::oct
    // 作用：控制整数的基数（十进制、十六进制、八进制）。
    std::cout << std::dec << 255 << std::endl; // 输出：255
    std::cout << std::hex << 255 << std::endl; // 输出：ff
    std::cout << std::oct << 255 << std::endl; // 输出：377

    // std::fixed 和 std::scientific
    // 作用：控制浮点数的表示形式（定点或科学计数法）。
    std::cout << std::fixed << 123.456 << std::endl; // 输出：123.456000
    std::cout << std::scientific << 123.456 << std::endl; // 输出：1.234560e+02

    // std::setprecision
    // 作用：设置浮点数的精度。
    std::cout << std::setprecision(5) << 123.456 << std::endl; // 输出：123.46

    // std::setw
    // 作用：设置输出字段的宽度。
    std::cout << std::setw(10) << 123 << std::endl; // 输出：       123

    // std::setfill
    // 作用：设置填充字符。
    std::cout << std::setfill('*') << std::setw(10) << 123 << std::endl; // 输出：*******123

    // std::left、std::right 和 std::internal
    // 作用：控制对齐方式（左对齐、右对齐、符号和数值分开对齐） 。
    std::cout << std::left << std::setw(10) << 123 << std::endl; // 输出：123
    std::cout << std::right << std::setw(10) << 123 << std::endl; // 输出：       123
    std::cout << std::internal << std::setw(10) << -123 << std::endl; // 输出：-       123


    ////////////////////////////////////////////////////////////
    ///

    // 在 C++ 标准库中，提供了一组低层次的未格式化 I/O 操作。
    // 这些操作允许将一个流当作无解释的字节序列来处理，而不是按格式化的方式进行输入输出。
    // 这些未格式化的 I/O 操作主要用于文件流对象（fstream）以及标准输入输出流对象（cin、cout）。

    // istream::get
    // 作用：从输入流中读取一个字符，或者读取一个指定长度的字符数组。
    std::ifstream infile("example.txt");
    char buffer[100];
    char ch;

    infile.get(ch); // 读取一个字符到 ch 中
    infile.get(buffer, 100); // 读取最多 99 个字符到 buffer 中，保留一个空终止符

    // istream::getline
    // 作用：从输入流中读取一行字符，并存储到字符数组中，直到遇到换行符或者达到指定长度。
    infile.getline(buffer, 100);  // 读取一行字符到 buffer 中

    // istream::read
    // 作用：从输入流中读取指定数量的字符，并存储到字符数组中。
    infile.read(buffer, 100);  // 读取 100 个字符到 buffer 中

    // istream::ignore
    // 作用：从输入流中忽略指定数量的字符，或者忽略直到遇到指定的终止字符。
    infile.ignore(10, '\n');  // 忽略最多 10 个字符，或者忽略到换行符

    // istream::peek
    // 作用：从输入流中读取下一个字符，但不提取该字符（即该字符仍然留在输入流中）。
    ch = infile.peek();  // 读取下一个字符但不提取

    // istream::putback
    // 作用：将上一个读取的字符放回输入流中。
    infile.get(ch);
    infile.putback(ch);  // 将字符放回输入流中

    // istream.gcount() 我们可以调用gcount来确定最后一个未格式化输入操作读取了多少个字符
    int read_count = cin.gcount();
    /**
     * 某些操作从输入读取未知个数的字节。 我们可以调用gcount来确定最后一个未格式
     * 化输入操作读取了多少个字符。应该在任何后续未格式化输入操作之前调用gcount。 特
     * 别是， 将字符退回流的单字符操作也屈千未格式化输入操作。 如果在调用gcount之前调
     * 用了peek、 unget或putback, 则gcount 的返回值为 0。
     */

    //////////////////////////////////////////////////////////////////////////////
    ///
    ///

    /**
    在 C++ 中，使用 char 变量来读取字符流可能会导致意想不到的问题，特别是在处理文件结束符（EOF）时。以下是详细的解释：

    使用 char 读取字符的问题
    std::istream::get() 返回一个 int，而不是 char。这是为了能够区分所有可能的字符值和特殊的 EOF 值。
    EOF 通常定义为一个负值（通常是 -1）。

    如果我们将 std::istream::get() 的返回值存储在一个 char 变量中，可能会遇到以下问题：

    字符转换：char 类型在不同平台上可能是有符号（signed）或者无符号（unsigned）的。
    在有符号 char 中，EOF（-1）可以被正确表示。
    但是在无符号 char 中，EOF 会被转换成一个正值（通常是 255），这将导致判断条件失效，无法正确检测到 EOF。

    即使 char 是有符号类型，也可能在读取非 ASCII 字符（如扩展 ASCII 字符）时导致数据丢失或误判。
    使用 int 类型存储 std::istream::get() 的返回值可以避免因字符类型转换导致的 EOF 检测问题。
    在读取和处理字符流时，这种方法是推荐的做法，可以确保程序的正确性和可移植性。
    */

    // 为了避免上述问题，应该使用 int 类型来存储 std::istream::get() 的返回值。
    // 这样可以确保 EOF 被正确检测到，因为 int 可以表示所有可能的字符值（0-255）以及 EOF（-1）。
    int ch2;
    while ((ch2 = std::cin.get()) != EOF) {
        std::cout.put(static_cast<char>(ch2));
    }


    return 0;
}
